package com.author.surface;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;

import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import com.author.menubar.MenuBar;
import com.author.util.OSValidator;

/**
 * This is the class that will initialize the Application Surface. It will
 * consists of a Tool Bar at the top and a Text Panel at the center of the
 * frame. A Status Bar is present at the bottom. The Scroll Pane added to it
 * will set its Viewport to watch the growth of the middle Text Panel and
 * appear/disappear according to the growth of the Text Panel.
 */
public final class Surface extends JFrame {
	/** X and Y location of the surface frame relative to the display device */
	private static final int	APPL_SURFACE_X_POSITION	= 100;
	private static final int	APPL_SURFACE_Y_POSITION	= 0;

	/** Width and height of the application surface */
	private int					APPL_SURFACE_WIDTH		= 800;
	private int					APPL_SURFACE_HEIGHT		= 930;

	/** Surface color of the master frame */
	private static final Color	APPL_SURFACE_COLOR		= Color.gray;

	/** Name of the application */
	private static final String	APPL_NAME				= "Author";

	/** The toolPanel contains the Tool Bar */
	private ToolPanel			toolPanel				= null;

	/** Width and height of the Tool Panel */
	private int					TOOLPANEL_WIDTH			= 0;
	private int					TOOLPANEL_HEIGHT		= 0;

	/**
	 * The textPanel is the middle panel inside the Application surface. It
	 * contains the Canvas Pane and inside it the main Text Pane will reside
	 */
	private TextPanel			textPanel				= null;

	/** Width and height of the Text Panel */
	private int					TEXTPANEL_WIDTH			= 0;
	private int					TEXTPANEL_HEIGHT		= 0;

	/**
	 * The Status Bar will be used for displaying information and warning
	 * messages to the user
	 */
	private StatusBar			statusBar;

	/** ScrollPane will give scrolling capability to the middle text panel */
	private JScrollPane			scrollPane;

	/** Menu Bar for the surface frame */
	private JMenuBar			menuBar;

	/** Only a single instance of Application surface is required */
	private static boolean		SINGLE_INSTANCE			= true;

	/** Application Surface of the Author document authoring tool */
	private static Surface		ApplicationSurface;

	protected Surface() {
		/*
		 * Set Application Look And Feel (the detailed strategy will be decided
		 * later for customizing Look And Feel for various platform)
		 */
		setApplicationLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		JFrame.setDefaultLookAndFeelDecorated(true);
		SwingUtilities.updateComponentTreeUI(this);

		/* Set the size and location of the Application Surface frame */
		setSize(new Dimension(APPL_SURFACE_WIDTH, APPL_SURFACE_HEIGHT));
		setPreferredSize(getSize());
		setBounds(new Rectangle(APPL_SURFACE_X_POSITION,
				APPL_SURFACE_Y_POSITION, APPL_SURFACE_WIDTH,
				APPL_SURFACE_HEIGHT));

		/* Set the background color and title of the Application Surface */
		setBackground(APPL_SURFACE_COLOR);
		setTitle(APPL_NAME);

		/*
		 * Set background color and layout of the application surface content
		 * pane
		 */
		getContentPane().setLayout(new BorderLayout());
		getContentPane().setBackground(APPL_SURFACE_COLOR);

		/*
		 * Add the TOOL PANEL, TEXT PANEL and the SCROLL PANE to the surface.
		 * Also add the Status Bar at the bottom
		 */
		addToolPanel();
		addStatusBar();
		addTextPanel();
		addScrollPane();

		/* Create a menu bar for the application surface */
		createMenuBar();

		/* Set frame visibility, focus and close operation properties */
		setVisible(true);
		pack();
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	/* Creates and sets a menu bar for the application surface */
	private void createMenuBar() {
		this.menuBar = new MenuBar();
		this.setJMenuBar(this.menuBar);
	}

	/**
	 * The Application Surface is a singleton design pattern. Only one instance
	 * of the application surface is present at a time.
	 * 
	 * @return Surface, the Application surface
	 */
	public static Surface createApplicationSurface() throws Exception {
		if (Surface.SINGLE_INSTANCE) {
			Surface.SINGLE_INSTANCE = false;
			ApplicationSurface = new Surface();
			return ApplicationSurface;
		} else {
			throw new Exception(
					"Surface creation exception, multiple surface requests have been received!");
		}
	}

	/* Attach a Tool Panel to the Application Design Surface */
	private void addToolPanel() {
		try {
			/*
			 * The width of the Tool Panel is same as that of the application
			 * surface. The height of the Tool Panel will be 8% of the height of
			 * the application surface
			 */
			toolPanel = new ToolPanel(getApplicationSurfaceWidth(),
					(int) (getApplicationSurfaceHeight() * 0.08));
			getContentPane().add(toolPanel, BorderLayout.NORTH);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/* Attach a Text Panel to the Application Design Surface */
	private void addTextPanel() {
		try {
			/*
			 * The width of the Text Panel is same as that of the application
			 * surface. The height of the Text Panel will be 90% of the height
			 * of the application surface
			 */
			textPanel = new TextPanel(getApplicationSurfaceWidth(),
					(int) (getApplicationSurfaceHeight() * 0.9));
			getContentPane().add(textPanel, BorderLayout.CENTER);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/*
	 * Attach a SCROLL PANE to the Application Surface and set the Viewport of
	 * the pane to watch the Text Panel
	 */
	private void addScrollPane() {
		scrollPane = new JScrollPane();
		textPanel.add(scrollPane);
		scrollPane.setViewportView(this.textPanel.getCanvasPanel());
	}

	/*
	 * Attach a Status Bar to the Application Design Surface. The height of the
	 * Status Bar should be at most equal to 2% of the Application Design
	 * Surface
	 */
	private void addStatusBar() {
		statusBar = new StatusBar(getApplicationSurfaceWidth(),
				(int) (getApplicationSurfaceHeight() * 0.03));
		getContentPane().add(statusBar, BorderLayout.SOUTH);
	}

	public int getApplicationSurfaceWidth() {
		return this.APPL_SURFACE_WIDTH;
	}

	public int getApplicationSurfaceHeight() {
		return this.APPL_SURFACE_HEIGHT;
	}

	public int getToolPanelHeight() {
		return this.TOOLPANEL_HEIGHT;
	}

	public int getTextPanelHeight() {
		return this.TEXTPANEL_HEIGHT;
	}

	public JPanel getTextPanel() {
		return this.textPanel;
	}

	public JPanel getToolPanel() {
		return this.toolPanel;
	}

	public static JFrame getAppSurface() {
		return Surface.ApplicationSurface;
	}

	/*
	 * This method will be used to set the Application Look and Feel. Currently
	 * this method is not called, which means that the application is always
	 * displayed using the default Java Look And Feel (Metal)
	 */
	private void setApplicationLookAndFeel(String LookAndFeel) {
		OSValidator osName = new OSValidator();
		if (OSValidator.isMac()) {
			try {
				UIManager.setLookAndFeel(LookAndFeel);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (UnsupportedLookAndFeelException e) {
				e.printStackTrace();
			}
		} else if (OSValidator.isUnix()) {
			try {
				UIManager
						.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (UnsupportedLookAndFeelException e) {
				e.printStackTrace();
			}
		} else { // set LAF to motif LAF for all other OS
			try {
				UIManager
						.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (UnsupportedLookAndFeelException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * @return Get the name of the Application
	 */
	public String getApplicationName() {
		return Surface.APPL_NAME;
	}
}
